package lumis.service.wsrp.consumer.doui;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import lumis.doui.source.IDataProvider;
import lumis.doui.source.ISourceData;
import lumis.doui.source.TabularData;
import lumis.doui.source.TabularSource;
import lumis.doui.source.field.ITabularSourceField;
import lumis.portal.PortalException;
import lumis.portal.UnexpectedException;
import lumis.portal.authentication.SessionConfig;
import lumis.service.wsrp.consumer.IPortletServiceManager;
import lumis.service.wsrp.consumer.PortletServiceManagerFactory;
import lumis.service.wsrp.consumer.producermanager.ClonedPortlet;
import lumis.service.wsrp.consumer.producermanager.ProducerEntityImpl;
import lumis.service.wsrp.consumer.producermanager.ProducerEntityManagerFactory;
import lumis.util.ITransaction;

import com.sun.portal.wsrp.common.WSRPUtility;
import com.sun.portal.wsrp.common.stubs.v2.LocalizedString;
import com.sun.portal.wsrp.common.stubs.v2.PortletDescription;
import com.sun.portal.wsrp.common.stubs.v2.Resource;
import com.sun.portal.wsrp.common.stubs.v2.ResourceValue;
import com.sun.portal.wsrp.common.stubs.v2.ServiceDescription;
import com.sun.portal.wsrp.consumer.common.WSRPConsumerException;
import com.sun.portal.wsrp.consumer.producermanager.ProducerEntity;
import com.sun.portal.wsrp.consumer.producermanager.ProducerEntityManager;

/**
 * Reads offered portlets by producer and populate a {@link TabularData} with
 * them.
 * 
 * @version $Revision: 12864 $ $Date: 2011-04-29 16:18:20 -0300 (Fri, 29 Apr 2011) $
 * @since 5.5.0
 */
public class RemotePortletConfigurationInterfaceDataProvider implements IDataProvider<TabularSource<ITabularSourceField>>
{
	public void loadData(SessionConfig sessionConfig,
						TabularSource<ITabularSourceField> source,
						ITransaction transaction)
					throws PortalException
	{
		String serviceInstanceId = source.getDouiContext().getRequest().getServiceInstanceConfig().getId();
		
		// gets caller service interface instance id to get its custom
		// properties
		String callerServiceInterfaceInstanceId = source.getDouiContext().getRequest().getCallerServiceInterfaceInstanceId();
		if (callerServiceInterfaceInstanceId == null)
			throw new PortalException("STR_NULL_CALLER_SERVICE_INTERFACE_INSTANCE_ID");

		// obtain producer entity
		ProducerEntity producerEntity = obtainProducerEntity(serviceInstanceId);

		// if current service instance is configured
		if (producerEntity != null)
		{
			Locale locale = source.getSourceContext().getSessionConfig().getLocale();
			
			fillTabularDataOfferedPortlets(source.getData(), producerEntity, locale);			
			fillTabularDataClonedPortlets(source.getData(), (ProducerEntityImpl) producerEntity);
			
			source.applyPostLoadFilters();
			source.getData().sort(source);
		}
	}
	
	private void fillTabularDataOfferedPortlets(TabularData tabularData, ProducerEntity producerEntity, Locale locale)
	{
		ServiceDescription serviceDescription = producerEntity.getServiceDescription();
		
		for (PortletDescription portletDescription : serviceDescription.getOfferedPortlets())
		{
			ISourceData row = tabularData.addRow();
			
			// portletHandle
			String portletHandle = portletDescription.getPortletHandle();
			row.put("portletHandle", portletHandle);
			
			// displayName
			LocalizedString displayName = portletDescription.getDisplayName();
			if (displayName == null)
			{
				displayName = portletDescription.getShortTitle();
				if (displayName == null)
					displayName = portletDescription.getTitle();
			}
			String displayNameValue;
			if (displayName != null)
				displayNameValue = localize(serviceDescription, displayName, locale);
			else
				displayNameValue = portletHandle;
			row.put("displayName", displayNameValue);
			
			// description
			LocalizedString description = portletDescription.getDescription();
			if (description != null)
				row.put("description", localize(serviceDescription, description, locale));
			
			row.put("friendlyName", "N/A");
		}
	}

	private void fillTabularDataClonedPortlets(
					TabularData tabularData,
					ProducerEntityImpl producerEntity)
				throws PortalException
	{
		Set<ClonedPortlet> clonedPortletsSet = new HashSet<ClonedPortlet>();
		if (producerEntity.getRegistration() != null)
		{
			clonedPortletsSet = producerEntity.getRegistration().getClonedPortlets();
			IPortletServiceManager portletServiceManager = PortletServiceManagerFactory.getPortletServiceManager(producerEntity.getName()); 
			for (ClonedPortlet clonedPortlet : clonedPortletsSet)
			{
				ISourceData row = tabularData.addRow();
				
				String displayName = portletServiceManager.getPortletName(clonedPortlet.getPortletHandle());
				String description = portletServiceManager.getDescriptionFromPortlet(clonedPortlet.getPortletHandle());
				
				row.put("friendlyName", clonedPortlet.getName());
				row.put("portletHandle", clonedPortlet.getPortletHandle());
				row.put("displayName", (displayName != null)?displayName:"N/A");				
				row.put("description", description);
			}			
		}
	}
	
	/**
	 * Retrieves the producer entity.
	 * @param source	the source.
	 * @return			an instance of ProducerEntity.
	 * @throws UnexpectedException
	 */
	private ProducerEntity obtainProducerEntity(String serviceInstanceId)
						throws UnexpectedException
	{
		// check if current service instance is configured
		
		ProducerEntityManager producerEntityManager = ProducerEntityManagerFactory.getProducerEntityManager(serviceInstanceId);
		ProducerEntity producerEntity = null;
		
		try
		{
			if (!(producerEntityManager.getProducerEntityIds().isEmpty()))
			{
				producerEntity = producerEntityManager.getProducerEntity(producerEntityManager.getProducerEntityIds().iterator().next());
			}
		}
		catch (WSRPConsumerException e)
		{
			throw new UnexpectedException(e);
		}
		return producerEntity;
	}
	
	/**
	 * Localizes a String to the given locale, according to the resources in
	 * the service description.
	 * @param serviceDescription the service description.
	 * @param localizedString the localized string.
	 * @param locale the locale.
	 * @return the string to be used for the given locale.
	 * @since 5.5.0
	 */
	private String localize(ServiceDescription serviceDescription, 
			LocalizedString localizedString, Locale locale)
	{
		// find the resource
		String resourceName = localizedString.getResourceName();
		Resource resource = null;
		for (Resource r: serviceDescription.getResourceList().getResources())
		{
			if (resourceName.equals(r.getResourceName()))
			{
				resource = r;
				break;
			}
		}

		if (resource != null)
		{
			// try to get the resource value for the locale (exact match)
			List<ResourceValue> resourceValues = resource.getValues();
			ResourceValue resourceValue = getResourceValue(resourceValues, WSRPUtility.toXMLLang(locale.toString()));
			if (resourceValue == null)
			{
				// try to get the resource value for the locale specifying language only
				Locale onlyLanguage = new Locale(locale.getLanguage());
				resourceValue = getResourceValue(resourceValues, WSRPUtility.toXMLLang(onlyLanguage.toString()));
			}
			if (resourceValue != null)
				return resourceValue.getValue();
		}
		
		// fallback to the value in the localizedString
		return localizedString.getValue();
	}
	
	/**
	 * Returns the ResourceValue with the given xmlLang within the given collection.
	 * @param resourceValues the resource values collection to search within. 
	 * @param xmlLang the xmlLang.
	 * @return the ResourceValue, or <code>null</code> if none with the specified
	 * xmlLang was found.
	 * @since 5.5.0
	 */
	private ResourceValue getResourceValue(Collection<ResourceValue> resourceValues, String xmlLang)
	{
		for (ResourceValue resourceValue: resourceValues)
		{
			if (xmlLang.equals(resourceValue.getLang()))
				return resourceValue;
		}
		return null;
	}
}